1. /* sdfasin.cpp by K.Tsuru */
  2. // function ID 3102 DRADIX
  3. /**********************************************************************
  4. SDouble arcsin(x) for |x| <= 1.0
  5. [Algorithm]
  6. A.If x > 1/sqrt(2), using a formura
  7. arcsin x = pi/2 - arcsin ( sqrt(1-x*x) )
  8. it can be reduced into |x| < 1/sqrt(2).
  9. B.Pay attention a modified addition theorem
  10. arcsin(x) = arcsin(y) + arcsin(delta),
  11. where x, y >=0 and
  12. delta = x*sqrt(1-y^2) - y*sqrt(1-x^2).
  13. Because this is an identity, the value of "y" can be chosen arbitrarily
  14. within the domain.Taking x = y gives delta = 0.
  15. Here we think of the following idea.
  16. 1.In the double precision it evaluates x' = asin(x").Where the x" in rhs is
  17. a rough estimated value of x in double precision if long.
  18. 2.In the multi-precision it calculates y = Sin(x').The convergence of series
  19. Sin(x') is fast.Here an equation x' = arcsin(y) stands up in the multi-precision.
  20. 3.Evaluates "delta" in the multi-precision.
  21. 4.Using a function AsinSeries(x) which provides the value of arcsin x by series,
  22. Asin(x) = x' + AsinSeries(delta)
  23. is calculated.Because the value of "delta" is very small less than 10^(-15),
  24. we get a fast convergence of series.
  25. The algorithm mentioned above enables us to calculate arcsin x very rapidly.
  26. Most of processing time is used in the evaluation Sin(x').A similar method is
  27. used in the function Log(x).
  28. ****************************************************************************/
  29. #ifndef SN_H
  30. #include "sn.h"
  31. #endif
  32. #define usesAsinSeriesInAsin 1 // for debug
  33. /****************************************************************************
  34. An auxiliary function
  35. It receives an approximated value of arcsin x in "approX" and set an exact
  36. value within the present precision in it.
  37. *****************************************************************************/
  38. static void AsinApproX(const SDouble& x, SDouble& approX){
  39. RealSize C;
  40. SDouble y, Y;
  41. uint upPrec = 0;
  42. //If |x| << 1.0,temporally raise up the precition to accurately evaluate the
  43. //value of "x-Y (~= x - approX)" i.e."y" below.
  44. if(x.NetRdxExp() < 0){
  45. y = x-approX;
  46. upPrec = (uint)abs(y.NetRdxExp());
  47. upPrec = y.ProperUpPrec(upPrec);
  48. if(upPrec) C.SetEffFig(x.EffFig() + upPrec, C.TEMP_EXTEND);
  49. }
  50. Y = Sin(approX); //Here use much time, about 80% of total one. "SinBS(x)" is called
  51. // y = x*Sqrt(ONE - Y*Y) + Y*Sqrt(ONE - x*x); y = (x-Y)*(x+Y)/y;
  52. y = x*Sqrt(ONE - Y*Y) - Y*Sqrt(ONE - x*x); //same precision above
  53. if(upPrec) C.SetEffFig(0);
  54. //Set the fixed value of exponent equal to that of additional value "approX"
  55. //to avoid the evaluation of useless lower figures.
  56. y = AsinSeries(y, approX.RdxExp());
  57. approX += y;
  58. }
  59. /*********************************
  60. r = asin(x) for |x| < sqrt(0.5)
  61. **********************************/
  62. static void AsinForSmallX(const SDouble& x, SDouble& r){
  63. // If x is too small, UNDERFLOW_ERR in doubleD.
  64. double dblX = doubleD(x, 0); //rough estimate in double
  65. #if usesAsinSeriesInAsin
  66. double ef = double(x.EffFig()), c, absX = fabs(dblX);
  67. uint xfig = x.Last() - x.First() +1u;
  68. if(x.NetRdxExp() <= -20) c = -1.0; // |x| < 1.0e-80
  69. else if(xfig <= 5u){ // x is short.
  70. c = 0.75*log10(ef) + log10(absX) - 0.71;
  71. } else { // x is long but very small.
  72. c = ef/64.0 + 0.6*log10(absX) - 0.3;
  73. }
  74. // x is short or small, then call AsinSeries()
  75. if(c < 0){ r = AsinSeries(x); return; }
  76. #endif
  77. RealSize C;
  78. //changed on Oct 16, 2000
  79. uint efig = x.EffFig(), appFig = (efig < 4000u) ? 20u : 200u;
  80. uint fig = min(efig, appFig);
  81. r = asin(dblX);
  82. //An intermediate step is inserted.
  83. C.SetEffFig(fig);
  84. AsinApproX(x, r); //rough estimation in "fig" effective figures
  85. C.SetEffFig(0);
  86. if(x.EffFig() > fig) AsinApproX(x, r); //full precision
  87. }
  88. SDouble Asin(const SDouble& x){
  89. if(x.Sign(3102) == 0) return x; // x = 0
  90. // check |x| <= 1.0
  91. SDouble r, pi2;
  92. if(x.Sign() > 0) r = ONE - x; // must be r = 1.0 - |x| >= 0
  93. else r = ONE + x;
  94. if(r.Sign() < 0) x.SetError(x.DOMAIN_ERR, "Asin", 3102); // |x|>1.0
  95. if(r.Sign() == 0){ // |x| = 1.0
  96. pi2 = MPi2(); // pi2 = pi/2
  97. if(x.Sign() < 0) pi2.ChangeSign(); // pi2 = -pi2
  98. return pi2;
  99. }
  100. double dblX = doubleD(x, 0);
  101. if(fabs(dblX) >= M_SQRT_2){// |x| >= sqrt(0.5).
  102. //Using arcsin x = pi/2 - arcsin ( sqrt(1-x*x) )
  103. pi2 = MPi2(); // pi2 = pi/2
  104. //Using the present value of "r", get r = 1 - x*x.
  105. if(x.Sign() > 0) r *= (ONE + x);
  106. else r *= (ONE - x);
  107. SDouble x1;
  108. x1 = Sqrt(r);
  109. AsinForSmallX(x1, r); //do not use recursive call
  110. r = pi2 -r;
  111. if(x.Sign() < 0) r.ChangeSign(); // r = -r;
  112. } else {
  113. AsinForSmallX(x, r);
  114. }
  115. return r;
  116. }

sdfasin.cpp : last modifiled at 2016/08/29 16:52:41(4,713 bytes)
created at 2017/10/07 10:22:50
The creation time of this html file is 2017/10/07 11:29:39 (Sat Oct 07 11:29:39 2017).